package logger

import (
	"encoding/json"
	"fmt"
	"strings"
	"time"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/natefinch/lumberjack.v2"
)

type ZapConfig struct {
	Level             zap.AtomicLevel `json:"level" yaml:"level"`
	Development       bool            `json:"development" yaml:"development"`
	DisableCaller     bool            `json:"disableCaller" yaml:"disableCaller"`
	DisableStacktrace bool            `json:"disableStacktrace" yaml:"disableStacktrace"`
	Encoding          string          `json:"encoding" yaml:"encoding"`
	OutputPath        string          `json:"outputPath" yaml:"outputPath"`
}

// init log
func InitLogger(runmode, logPath string, maxSize, maxBackups, maxAge int, disableStacktrace bool) *zap.Logger {
	level := "debug"
	encoding := "json"
	development := true
	if runmode == "prod" {
		level = "info"
		development = false
	}

	js := fmt.Sprintf(`{
        "level": "%s",
        "encoding": "%s",
        "outputPath": "%s"
        }`, level, encoding, logPath)

	var zcfg ZapConfig
	if err := json.Unmarshal([]byte(js), &zcfg); err != nil {
		panic(err)
	}

	zcfg.Development = development
	zcfg.DisableStacktrace = disableStacktrace

	logger = zcfg.New(maxSize, maxBackups, maxAge)
	Logger = logger
	return logger
}

func NewInfoLogger(runmode, logPath string, maxSize, maxBackups, maxAge int) *zap.Logger {
	level := "info"
	encoding := "json"
	development := true
	if runmode == "prod" {
		development = false
	}

	js := fmt.Sprintf(`{
        "level": "%s",
        "encoding": "%s",
        "outputPath": "%s"
        }`, level, encoding, logPath)

	var zcfg ZapConfig
	if err := json.Unmarshal([]byte(js), &zcfg); err != nil {
		panic(err)
	}

	zcfg.Development = development
	zcfg.DisableStacktrace = true

	return zcfg.New(maxSize, maxBackups, maxAge)
}

func (zc *ZapConfig) New(maxSize, maxBackups, maxAge int) *zap.Logger {
	// if nil, default the values
	if maxSize == 0 {
		maxSize = 100
	}

	if maxBackups == 0 {
		maxBackups = 7
	}

	if maxAge == 0 {
		maxAge = 30
	}

	hook := lumberjack.Logger{
		Filename:   zc.OutputPath,
		MaxSize:    maxSize,    // megabytes
		MaxBackups: maxBackups, // backups number
		MaxAge:     maxAge,     // days
		Compress:   true,       // disabled by default
	}

	fileWriter := zapcore.AddSync(&hook)
	var EncoderConfig zapcore.EncoderConfig
	if zc.Development {
		EncoderConfig = zap.NewDevelopmentEncoderConfig()
	} else {
		EncoderConfig = zap.NewProductionEncoderConfig()
	}
	// 自定义
	EncoderConfig.EncodeTime = CustomTimeEncoder

	var encoder zapcore.Encoder
	if strings.ToLower(zc.Encoding) == "console" {
		encoder = zapcore.NewConsoleEncoder(EncoderConfig)
	} else {
		encoder = zapcore.NewJSONEncoder(EncoderConfig)
	}
	core := zapcore.NewCore(encoder, fileWriter, zc.Level)

	opts := zc.buildOptions(fileWriter)
	return zap.New(core, opts...)
}

func (zc *ZapConfig) buildOptions(fileWriter zapcore.WriteSyncer) []zap.Option {
	opts := []zap.Option{zap.ErrorOutput(fileWriter)}
	if !zc.DisableCaller {
		opts = append(opts, zap.AddCaller())
	}

	if !zc.DisableStacktrace {
		stackLevel := zap.ErrorLevel
		if zc.Development {
			stackLevel = zap.WarnLevel
		}

		opts = append(opts, zap.AddStacktrace(stackLevel))
	}

	return opts
}

// 自定义时间格式
func CustomTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
	enc.AppendString(t.Format("2006-01-02 15:04:05.000"))
}
